#define  _CRT_SECURE_NO_WARNINGS
//#include<condition_variable>
//#include<iostream>
//#include<mutex>
//#include<queue>
//using namespace std;
//
//
//template<class T>
//class Boundedbuffer
//{
//public:
//	Boundedbuffer(int n = 5)
//		:capacity(n)
//	{
//		
//	}
//	bool IsFull()
//	{
//		return _q.size()==capacity;
//	}
//	bool IsEmpty()
//	{
//		return _q.size() == 0;
//	}
//	void Push(const T& in)
//	{
//		unique_lock<mutex> lock(_mutex);
//		while (IsFull()) {
//			cout << "Buffer is full, producer is waiting." << endl;
//			_push_cond.wait(lock);
//		}
//		cout << "Buffer is not full, pushing element." << endl;
//		_q.push(in);
//		cout << "Element pushed, buffer size is now " << _q.size() << endl;
//		_pop_cond.notify_one();
//	}
//	void Pop(T* out)
//	{
//		unique_lock<mutex> lock(_mutex);
//		/*while (IsEmpty()) {
//			cout << "Buffer is empty, consumer is waiting." << endl;
//			_pop_cond.wait(lock);
//		}*/
//		_pop_cond.wait(lock, [this](){ return _q.size(); });
//		cout << "Buffer is not empty, popping element." << endl;
//		*out = _q.front();
//		_q.pop();
//		cout << "Element popped, buffer size is now " << _q.size() << endl;
//		_push_cond.notify_one();
//	}
//	~Boundedbuffer()
//	{
//	}
//
//private:
//	mutex _mutex;
//	condition_variable _push_cond;
//	condition_variable _pop_cond;
//	queue<T> _q;
//	int capacity;
//};
//
//int main()
//{
//	Boundedbuffer<int> buf(5);
//	thread thr([&]() {
//		int i = 0;
//		while (true)
//		{
//			buf.Pop(&i);
//			cout << i << endl;
//		}
//		});
//	for (int i = 0; i < 5; i++)
//		buf.Push(i);
//	thr.join();
//	return 0;
//}



#include<iostream>
#include<semaphore>
#include<vector>
#include<thread>
#include<mutex>
#include<chrono>

using namespace std;


template<class T>
class Queuebuffer
{
private:
	vector<T> _queue;
	int _capacity;
	counting_semaphore<int> _data_sem;
	counting_semaphore<int> _space_sem;
	mutex _push_mutex;
	mutex _pop_mutex;
	int _prodstep;
	int _condstep;
private:
	void P(counting_semaphore<int>& sem)
	{
		sem.acquire();
	}
	void V(counting_semaphore<int>& sem)
	{
		sem.release();
	}

public:
	Queuebuffer(int n)
		:_capacity(n),_space_sem(n),_data_sem(0),_prodstep(0),_condstep(0)
	{
		
	}

	void Push(const T& in)
	{
		P(_space_sem);
		{
			std::unique_lock<mutex> lock(_push_mutex);
			_queue[_prodstep] = in;
			_prodstep++;
			_prodstep %= _capacity;
		}
		V(_data_sem);
	}
	void Pop(T *out)
	{
		P(_data_sem);
		{
			std::unique_lock<mutex> lock(_pop_mutex);
			*out = _queue[_condstep];
			_condstep++;
			_condstep %= _capacity;
		}
		V(_space_sem);
	}

};


void* Consume(void *args)
{
	Queuebuffer<int>* rq = static_cast<Queuebuffer<int>*>(args);
	while (true)
	{
		int i = 0;
		rq->Pop(&i);
		cout << i << endl;
	}
}

void* Product(void *args)
{
	Queuebuffer<int>* rq = static_cast<Queuebuffer<int>*>(args);
	while (true)
	{
		int data1 = rand() % 10;
		rq->Push(data1);
		this_thread::sleep_for(chrono::seconds(1));
	}

}
int main()
{
	Queuebuffer<int> Q(5);
	thread thr1(Product, &Q);
	thread thr2(Product, &Q);
	thread thr3(Product, &Q);
	thread thr4(Consume, &Q);
	thread thr5(Consume, &Q);
	thread thr6(Consume, &Q);
	thread thr7(Consume, &Q);
	thr1.join();
	thr2.join();
	thr3.join();
	thr4.join();
	thr5.join();
	thr6.join();
	thr7.join();
	return 0;
}